const canvas = document.getElementById('game');
const context = canvas.getContext('2d');

const images = {};

function getImage(url) {
    return images[url]
}

function loadMultiImage(list, onSuccess) {
    let current = 0;
    const total = list.length;
    for (const item of list) {
        loadImage(item, function () {
            current++;
            if (current === total) {
                onSuccess();
            }
        })
    }
}

function loadImage(url, callback) {
    const image = new Image();
    image.src = url;
    image.onload = function () {
        images[url] = image;
        callback();
    }
}



class AbstractRenderer {

    /**
     * 父节点
     */
    parent = null;
    /**
     * x缩放
     */
    scaleX = 1;
    /**
     * y缩放
     */
    scaleY = 1;
    /**
     * 旋转
     */
    rotation = 0;
    /**
     * x平移
     */
    x = 0;
    /**
     * y平移
     */
    y = 0;


    /**
     * 是否可见
     */
    visible = true;

    /**
     * 透明度
     */
    alpha = 1;

    /**
     * 全局透明度
     */
    $globalAlpha = 1;

    /**
     * 相对矩阵
     */
    $localMatrix;

    /**
     * 绝对矩阵
     */
    $globalMatrix;


    constructor(x = 0, y = 0) {
        this.$localMatrix = new Matrix();
        this.$globalMatrix = new Matrix();
        this.x = x;
        this.y = y;
    }

    calculateGlobalProperties() {
        this.$localMatrix.updateFromDisplayObject(this.x, this.y, this.scaleX, this.scaleY, this.rotation);
        this.$globalMatrix = matrixAppendMatrix(this.$localMatrix, this.parent.$globalMatrix);
        this.$globalAlpha = this.parent.$globalAlpha * this.alpha;
    }

    draw(context) {
        this.calculateGlobalProperties();

        context.setTransform(
            this.$globalMatrix.a,
            this.$globalMatrix.b,
            this.$globalMatrix.c,
            this.$globalMatrix.d,
            this.$globalMatrix.tx,
            this.$globalMatrix.ty
        )
        context.globalAlpha = this.$globalAlpha;

        if (this.visible) {
            this.render(context);
        }

    }

    hitTest(point) {

    }

    render(context) {

    }
}

class DisplayObjectContainer extends AbstractRenderer {

    children = [];

    addChild(child) {
        const index = this.children.indexOf(child);
        child.parent = this;
        if (index == -1) {
            this.children.push(child);
        }
    }

    removeChild(child) {
        const index = this.children.indexOf(child);
        child.parent = null;
        if (index >= 0) {
            this.children.splice(index, 1);
        }
    }

    render(context) {
        for (let child of this.children) {
            child.draw(context);
        }
    }

    hitTest(point) {
        for (let i = this.children.length - 1; i >= 0; i--) {
            const child = this.children[i];
            const localMatrix = child.$localMatrix;
            const invertLocalMatrix = invertMatrix(localMatrix);
            //相对于子对象的相对坐标 = 相对于当前对象的相对坐标 * 子对象的相对矩阵的逆矩阵
            const childLocalPoint = pointAppendMatrix(point, invertLocalMatrix);
            const result = child.hitTest(childLocalPoint);
            if (result) {
                return result;
            }
        }
        return null;
    }
}

class RootContainer extends DisplayObjectContainer {

    calculateGlobalProperties() {

    }
}

class ImageRenderer extends AbstractRenderer {

    image = null;

    constructor(x, y, image) {
        super(x, y);
        this.image = image;
    }

    render(context) {
        const img = getImage(this.image)
        if (img) {
            context.drawImage(img, 0, 0);
        }
    }

    hitTest(point) {
        const img = getImage(this.image)
        if (!img) {
            return null;
        }
        const rectangle = {
            x: 0,
            y: 0,
            width: img.width,
            height: img.height
        };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this;
        } else {
            return null;
        }
    }

}

class TextRenderer extends AbstractRenderer {

    color = 'black';
    text = 'helloworld';
    fontSize = 16;
    textAlign = 'start';

    constructor(x, y, color = 'black', text = 'helloworld', fontSize = 16, textAlign = 'start') {
        super(x, y);
        this.color = color;
        this.text = text;
        this.fontSize = fontSize;
        this.textAlign = textAlign;
    }

    render(context) {
        context.fillStyle = this.color;
        context.font = `${this.fontSize}px serif`;
        context.textAlign = this.textAlign;
        context.fillText(this.text, this.x, this.y);
    }

    hitTest(point) {
        const rectangle = {
            x: 0,
            y: 0,
            width: 100,
            height: 20
        };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this;
        } else {
            return null;
        }
    }
}

class RectRenderer extends AbstractRenderer {


    width = 100;
    height = 100;
    color = 'black'

    constructor(x, y, width = 100, height = 100, color = 'black') {
        super(x, y);
        this.width = width;
        this.height = height;
        this.color = color;
    }

    render(context) {
        context.fillStyle = this.color;
        context.fillRect(0, 0, this.width, this.height);
    }

    hitTest(point) {
        const rectangle = {
            x: 0,
            y: 0,
            width: this.width,
            height: this.height
        };
        const result = checkPointInRectangle(point, rectangle);
        if (result) {
            return this;
        } else {
            return null;
        }
    }
}


class GameEngineCore {

    onStart;

    onUpdate;

    root;

    lastIterationTime = 0;

    lastAdvancedTime = 0;

    fps = 60;

    start() {
        this.root = new RootContainer();
        const imgArray = [];
        for (let i = 1; i <= 15; i++) {
            imgArray.push(`img/item${i}.png`);
        }
        loadMultiImage(imgArray, this.onStart);
        requestAnimationFrame(this.onFrame.bind(this));
    }

    hitTest(point) {
        const result = this.root.hitTest(point);
        if (result && result.onClick) {
            result.onClick();
        }
    }

    onFrame(advancedTime) {
        context.save();
        context.clearRect(0, 0, 1300, 800)
        const duringTime = advancedTime - this.lastAdvancedTime;
        this.lastAdvancedTime = advancedTime;
        this.lastIterationTime += duringTime;
        const frameTime = 1000 / this.fps;
        while (this.lastIterationTime >= frameTime) {
            this.lastIterationTime -= frameTime;
            this.onUpdate(advancedTime);
        }
        this.root.draw(context);
        context.restore();
        requestAnimationFrame(this.onFrame.bind(this));
    }

}

const core = new GameEngineCore();
setTimeout(function () {
    core.start();
}, 100);





window.addEventListener('click', function (e) {
    const mouseGlobalX = e.offsetX;
    const mouseGlobalY = e.offsetY;
    const point = {
        x: mouseGlobalX,
        y: mouseGlobalY
    };
    core.hitTest(point);


})